home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / aros / dos / readargs.c < prev    next >
C/C++ Source or Header  |  1996-09-13  |  13KB  |  471 lines

  1. /*
  2.     (C) 1995-96 AROS - The Amiga Replacement OS
  3.     $Id: readargs.c,v 1.5 1996/09/13 17:50:08 digulla Exp $
  4.     $Log: readargs.c,v $
  5.     Revision 1.5  1996/09/13 17:50:08  digulla
  6.     Use IPTR
  7.  
  8.     Revision 1.4  1996/08/16 14:04:08  digulla
  9.     Fixed bug in docs. Bugfix in code is still pending.
  10.  
  11.     Revision 1.3  1996/08/13 13:52:50  digulla
  12.     Replaced <dos/dosextens.h> by "dos_intern.h" or added "dos_intern.h"
  13.     Replaced __AROS_LA by __AROS_LHA
  14.  
  15.     Revision 1.2  1996/08/01 17:40:56  digulla
  16.     Added standard header for all files
  17.  
  18.     Desc:
  19.     Lang: english
  20. */
  21. #include <exec/memory.h>
  22. #include <clib/exec_protos.h>
  23. #include <dos/rdargs.h>
  24. #include <dos/dosextens.h>
  25. #include "dos_intern.h"
  26.  
  27. /*****************************************************************************
  28.  
  29.     NAME */
  30.     #include <clib/dos_protos.h>
  31.  
  32.     __AROS_LH3(struct RDArgs *, ReadArgs,
  33.  
  34. /*  SYNOPSIS */
  35.     __AROS_LHA(STRPTR,          template, D1),
  36.     __AROS_LHA(IPTR *,          array,    D2),
  37.     __AROS_LHA(struct RDArgs *, rdargs,   D3),
  38.  
  39. /*  LOCATION */
  40.     struct DosLibrary *, DOSBase, 133, Dos)
  41.  
  42. /*  FUNCTION
  43.     Parses the commandline, a given string or Input() and fills
  44.     an argument array according to the options template given.
  45.     The array must be initialized to the wanted defaults before
  46.     each call to ReadArgs(). If the rdargs argument is NULL
  47.     ReadArgs() tries to parse the commandline and continues
  48.     on the input channel if it just consists of a single '?',
  49.     prompting the user for input.
  50.  
  51.     INPUTS
  52.     template - Template string. The template string is given as
  53.            a number of options separated by ',' and modified
  54.            by '/' modifiers, e.g. 'NAME,WIDTH/N,HEIGHT/N'
  55.            means get a name string and two numbers (width and
  56.            height). The possible modifiers are:
  57.            /S Option is a switch. It may be either set or
  58.               left out.
  59.            /T Option is a boolean value. Requires an argument
  60.               which may be "ON", "YES" (setting the respective
  61.               argument to 1), "OFF" or "NO" (setting the
  62.               respective argument to 0).
  63.            /N Option is a number. Strings are not allowed.
  64.               If the option is optional, a pointer to the
  65.               actual number is returned. This is how you know
  66.               if it was really given.
  67.            /A Argument is required. If it is left out ReadArgs()
  68.               fails.
  69.            /K The keyword must be given when filling the option.
  70.               Normally it's skipped.
  71.            /M Multiple strings. The result is returned as a string
  72.               pointer array terminated with NULL. /M eats all strings
  73.               that don't fit into any other option. If there are
  74.               unfilled /A arguments after parsing they steal strings
  75.               from /M. This makes it possible to e.g. write a COPY
  76.               template like 'FROM/A/M,TO/A'. There may be only one
  77.               /M option in a template.
  78.            /F Eats the rest of the line even if there are option
  79.               keywords in it.
  80.     array     - Array to be filled with the result values. The array must
  81.            be intialized to the default values before calling
  82.            ReadArgs().
  83.     rdargs     - An optional RDArgs structure determinating the type of
  84.            input to process.
  85.  
  86.     RESULT
  87.     A handle for the memory allocated by ReadArgs(). Must be freed
  88.     with FreeArgs() later.
  89.  
  90.     NOTES
  91.  
  92.     EXAMPLE
  93.  
  94.     BUGS
  95.     The rdargs argument is currently ignored.
  96.  
  97.     SEE ALSO
  98.     FreeArgs(), Input()
  99.  
  100.     INTERNALS
  101.  
  102.     HISTORY
  103.     29-10-95    digulla automatically created from
  104.                 dos_lib.fd and clib/dos_protos.h
  105.  
  106. *****************************************************************************/
  107. {
  108.     __AROS_FUNC_INIT
  109.     __AROS_BASE_EXT_DECL(struct DosLibrary *,DOSBase)
  110.  
  111.     /* Allocated resources */
  112.     struct RDArgs *rda=NULL;
  113.     struct DAList *dalist=NULL;
  114.     UBYTE *flags=NULL;
  115.     STRPTR strbuf=NULL, iline=NULL;
  116.     STRPTR *multvec=NULL, *argbuf=NULL;
  117.     ULONG multnum=0, multmax=0;
  118.  
  119.     /* Some variables */
  120.     STRPTR s1, s2, *newmult;
  121.     ULONG arg, numargs, nextarg;
  122.     LONG it, item, chars, value;
  123.     struct CSource cs;
  124.  
  125.     /* Get pointer to process structure. */
  126.     struct Process *me=(struct Process *)FindTask(NULL);
  127.  
  128.     /* Error recovery. C has no exceptions. This is a simple replacement. */
  129.     LONG error;
  130. #define ERROR(a) { error=a; goto end; }
  131.  
  132. /* Template options */
  133. #define REQUIRED 0x80 /* /A */
  134. #define KEYWORD  0x40 /* /K */
  135. #define TYPEMASK 0x07
  136. #define NORMAL     0x00 /* No option */
  137. #define SWITCH     0x01 /* /S, implies /K */
  138. #define TOGGLE     0x02 /* /T, implies /K */
  139. #define NUMERIC  0x03 /* /N */
  140. #define MULTIPLE 0x04 /* /M */
  141. #define REST     0x05 /* /F */
  142.  
  143.     /* Flags for each possible character. */
  144.     static const UBYTE argflags[]=
  145.     { REQUIRED, 0, 0, 0, 0, REST, 0, 0, 0, 0, KEYWORD, 0, MULTIPLE,
  146.       NUMERIC, 0, 0, 0, 0, SWITCH|KEYWORD, TOGGLE|KEYWORD, 0, 0, 0, 0, 0, 0 };
  147.  
  148.     /* Allocate readargs structure (and private internal one) */
  149.     rda=(struct RDArgs *)AllocVec(sizeof(struct RDArgs),MEMF_ANY);
  150.     dalist=(struct DAList *)AllocVec(sizeof(struct DAList),MEMF_ANY);
  151.     if(rda==NULL||dalist==NULL)
  152.     ERROR(ERROR_NO_FREE_STORE);
  153.  
  154.     /* Init character source. */
  155.     cs.CS_Buffer=me->pr_Arguments;
  156.     s1=cs.CS_Buffer;
  157.     while(*s1++)
  158.     ;
  159.     cs.CS_Length=(IPTR)s1-(IPTR)cs.CS_Buffer-1;
  160.     cs.CS_CurChr=0;
  161.  
  162.     /* Check commandline for a single '?' */
  163.     s1=cs.CS_Buffer;
  164.     /* Skip leading whitespace */
  165.     while(*s1==' '||*s1=='\t')
  166.     s1++;
  167.     /* Check for '?' */
  168.     if(*s1++=='?')
  169.     {
  170.     /* Skip whitespace */
  171.     while(*s1==' '||*s1=='\t')
  172.         s1++;
  173.     /* Check for EOL */
  174.     if(*s1=='\n'||!*s1)
  175.     {
  176.         /* Only a single '?' on the commandline. */
  177.         BPTR input=me->pr_CIS, output=me->pr_COS;
  178.         ULONG isize=0, ibuf=0;
  179.         LONG c;
  180.         /* Prompt for more input */
  181.         if(FPuts(output,template)||FPuts(output,": ")||!Flush(output))
  182.            ERROR(me->pr_Result2);
  183.         /* Read a line in. */
  184.         for(;;)
  185.         {
  186.         if(isize>=ibuf)
  187.         {
  188.             /* Buffer too small. Get a new one. */
  189.             STRPTR newiline;
  190.             ibuf+=256;
  191.             newiline=(STRPTR)AllocVec(ibuf,MEMF_ANY);
  192.             if(newiline==NULL)
  193.             ERROR(ERROR_NO_FREE_STORE);
  194.             CopyMemQuick((ULONG *)iline,(ULONG *)newiline,isize);
  195.             FreeVec(iline);
  196.             iline=newiline;
  197.         }
  198.         /* Read character */
  199.         c=FGetC(input);
  200.         /* Check and write it. */
  201.         if(c==EOF&&me->pr_Result2)
  202.             ERROR(me->pr_Result2);
  203.         if(c==EOF||c=='\n'||!c)
  204.             break;
  205.         iline[isize++]=c;
  206.         }
  207.         /* Prepare input source for new line. */
  208.         cs.CS_Buffer=iline;
  209.         cs.CS_Length=isize;
  210.     }
  211.     }
  212.  
  213.     /*
  214.     Get enough space for string buffer.
  215.     It's always smaller than the size of the input line+1.
  216.     */
  217.     strbuf=(STRPTR)AllocVec(cs.CS_Length+1,MEMF_ANY);
  218.     if(strbuf==NULL)
  219.     ERROR(ERROR_NO_FREE_STORE);
  220.  
  221.     /* TODO: rdargs!=NULL */
  222.  
  223.     /* Count the number of items in the template (number of ','+1). */
  224.     numargs=1;
  225.     s1=template;
  226.     while(*s1)
  227.     if(*s1++==',')
  228.         numargs++;
  229.  
  230.     /* Use this count to get space for temporary flag array and result buffer. */
  231.     flags=(UBYTE *)AllocVec(numargs+1,MEMF_CLEAR);
  232.     argbuf=(STRPTR *)AllocVec((numargs+1)*sizeof(STRPTR),MEMF_CLEAR);
  233.     if(flags==NULL||argbuf==NULL)
  234.     ERROR(ERROR_NO_FREE_STORE);
  235.  
  236.     /* Fill the flag array. */
  237.     s1=template;
  238.     s2=flags;
  239.     while(*s1)
  240.     {
  241.     /* A ',' means: goto next item. */
  242.     if(*s1==',')
  243.         s2++;
  244.     /* In case of a '/' use the next character as option. */
  245.     if(*s1++=='/')
  246.         *s2|=argflags[*s1-'A'];
  247.     }
  248.     /* Add a dummy so that the whole line is processed. */
  249.     *++s2=MULTIPLE;
  250.  
  251.     /*
  252.     Now process commandline for the first time:
  253.     * Go from left to right and fill all items that need filling.
  254.     * If an item is given as 'OPTION=VALUE' or 'OPTION VALUE' fill
  255.       it out of turn.
  256.     */
  257.     s1=strbuf;
  258.     for(arg=0;arg<=numargs;arg=nextarg)
  259.     {
  260.     nextarg=arg+1;
  261.  
  262.     /* Skip /K options and options that are already done. */
  263.     if(flags[arg]&KEYWORD||argbuf[arg]!=NULL)
  264.         continue;
  265.  
  266.     /* If the current option is of type /F do not look for keywords */
  267.     if((flags[arg]&TYPEMASK)!=REST)
  268.     {
  269.         /* Get item. Quoted items are no keywords. */
  270.         it=ReadItem(s1,~0ul/2,&cs);
  271.         if(it==ITEM_UNQUOTED)
  272.         {
  273.         /* Not quoted. Check if it's a keyword. */
  274.         item=FindArg(template,s1);
  275.         if(item>=0&&argbuf[item]==NULL)
  276.         {
  277.             /*
  278.             It's a keyword. Fill it and retry the current option
  279.             at the next turn
  280.             */
  281.             nextarg=arg;
  282.             arg=item;
  283.  
  284.             /* /S /T and /F may not be given as 'OPTION=VALUE'. */
  285.             if((flags[item]&TYPEMASK)!=SWITCH&&
  286.                (flags[item]&TYPEMASK)!=TOGGLE&&
  287.                (flags[item]&TYPEMASK)!=REST)
  288.             {
  289.             /* Get value. */
  290.             it=ReadItem(s1,~0ul/2,&cs);
  291.             if(it==ITEM_EQUAL)
  292.                 it=ReadItem(s1,~0ul/2,&cs);
  293.             }
  294.         }
  295.         }
  296.         /* Check returncode of ReadItem(). */
  297.         if(it==ITEM_EQUAL)
  298.         ERROR(ERROR_BAD_TEMPLATE);
  299.         if(it==ITEM_ERROR)
  300.         ERROR(me->pr_Result2);
  301.         if(it==ITEM_NOTHING)
  302.         break;
  303.     }
  304.     /* /F takes all the rest */
  305.     if((flags[arg]&TYPEMASK)==REST)
  306.     {
  307.         /* Skip leading whitespace */
  308.         while(cs.CS_CurChr<cs.CS_Length&&
  309.           (cs.CS_Buffer[cs.CS_CurChr]==' '||
  310.            cs.CS_Buffer[cs.CS_CurChr]=='\t'))
  311.         cs.CS_CurChr++;
  312.  
  313.         /* Find the last non-whitespace character */
  314.         s2=s1-1;
  315.         argbuf[arg]=s1;
  316.         while(cs.CS_CurChr<cs.CS_Length&&
  317.           cs.CS_Buffer[cs.CS_CurChr]&&
  318.           cs.CS_Buffer[cs.CS_CurChr]!='\n')
  319.         {
  320.         if(cs.CS_Buffer[cs.CS_CurChr]!=' '&&
  321.            cs.CS_Buffer[cs.CS_CurChr]!='\t')
  322.             s2=s1;
  323.         /* Copy string by the way. */
  324.         *s1++=cs.CS_Buffer[cs.CS_CurChr++];
  325.         }
  326.         /* Add terminator (1 after the character found). */
  327.         s2[1]=0;
  328.         it=ITEM_NOTHING;
  329.         break;
  330.     }
  331.     /* /S or /T just set a flag */
  332.     if((flags[arg]&TYPEMASK)==SWITCH||(flags[arg]&TYPEMASK)==TOGGLE)
  333.         argbuf[arg]=(char *)1;
  334.     else if((flags[arg]&TYPEMASK)==MULTIPLE)
  335.     {
  336.         /* All /M arguments are stored in a buffer. */
  337.         if(multnum>=multmax)
  338.         {
  339.         /* Buffer too small. Get a new one. */
  340.         multmax+=16;
  341.         newmult=(STRPTR *)AllocVec(multmax*sizeof(char *),MEMF_ANY);
  342.         if(newmult==NULL)
  343.             ERROR(ERROR_NO_FREE_STORE);
  344.         CopyMemQuick((ULONG *)multvec,(ULONG *)newmult,multnum*sizeof(char *));
  345.         FreeVec(multvec);
  346.         multvec=newmult;
  347.         }
  348.         /* Put string into the buffer. */
  349.         multvec[multnum++]=s1;
  350.         while(*s1++)
  351.         ;
  352.         /* /M takes more than one argument, so retry. */
  353.         nextarg=arg;
  354.     }else /* NORMAL || NUMERIC */
  355.     {
  356.         /* Put argument into argument buffer. */
  357.         argbuf[arg]=s1;
  358.         while(*s1++)
  359.         ;
  360.     }
  361.     }
  362.  
  363.     /* Unfilled /A options steal Arguments from /M */
  364.     for(arg=numargs;arg-->0;)
  365.     if(flags[arg]&REQUIRED&&argbuf[arg]==NULL&&
  366.        (flags[arg]&TYPEMASK)!=MULTIPLE)
  367.     {
  368.         if(!multnum)
  369.         /* No arguments left? Oh dear! */
  370.         ERROR(ERROR_REQUIRED_ARG_MISSING);
  371.         argbuf[arg]=multvec[--multnum];
  372.     }
  373.  
  374.     /* Put the rest of /M where it belongs */
  375.     for(arg=0;arg<numargs;arg++)
  376.     if((flags[arg]&TYPEMASK)==MULTIPLE)
  377.     {
  378.         if(flags[arg]&REQUIRED&&!multnum)
  379.         ERROR(ERROR_REQUIRED_ARG_MISSING);
  380.  
  381.         /* NULL terminate it. */
  382.         if(multnum>=multmax)
  383.         {
  384.         multmax+=16;
  385.         newmult=(STRPTR *)AllocVec(multmax*sizeof(STRPTR),MEMF_ANY);
  386.         if(newmult==NULL)
  387.             ERROR(ERROR_NO_FREE_STORE);
  388.         CopyMemQuick((ULONG *)multvec,(ULONG *)newmult,multnum*sizeof(char *));
  389.         FreeVec(multvec);
  390.         multvec=newmult;
  391.         }
  392.         multvec[multnum++]=NULL;
  393.         argbuf[arg]=(STRPTR)multvec;
  394.         break;
  395.     }
  396.  
  397.     /* There are some arguments left? Return error. */
  398.     if(multnum&&arg==numargs)
  399.     ERROR(ERROR_TOO_MANY_ARGS);
  400.  
  401.     /*
  402.     The commandline is processed now. Put the results in the result array.
  403.     Convert /N arguments by the way.
  404.     */
  405.     for(arg=0;arg<numargs;arg++)
  406.     {
  407.     /* Just for the arguments given. */
  408.     if(argbuf[arg]!=NULL)
  409.     {
  410.         switch(flags[arg]&TYPEMASK)
  411.         {
  412.         case NORMAL:
  413.         case MULTIPLE:
  414.         case REST:
  415.         case SWITCH:
  416.             /* Simple arguments are just copied. */
  417.             array[arg]=(IPTR)argbuf[arg];
  418.             break;
  419.         case TOGGLE:
  420.             /* /T logically inverts the argument. */
  421.             array[arg]=!array[arg];
  422.             break;
  423.         case NUMERIC:
  424.             /* Convert /N argument. */
  425.             chars=StrToLong(argbuf[arg],&value);
  426.             if(chars<=0||argbuf[arg][chars])
  427.             /* Conversion failed. */
  428.             ERROR(ERROR_BAD_NUMBER);
  429.             /* Put the result where it belongs. */
  430.             if(flags[arg]&REQUIRED)
  431.             /* Required argument. Return number. */
  432.             array[arg]=value;
  433.             else
  434.             {
  435.             /* Abuse the argbuf buffer. It's not needed anymore. */
  436.             argbuf[arg]=(STRPTR)value;
  437.             array[arg]=(IPTR)&argbuf[arg];
  438.             }
  439.             break;
  440.         }
  441.     }
  442.     }
  443.  
  444.     /* All OK. */
  445.     error=0;
  446. end:
  447.     /* Cleanup and return. */
  448.     FreeVec(iline);
  449.     FreeVec(flags);
  450.     if(error)
  451.     {
  452.     /* ReadArgs() failed. Clean everything up. */
  453.     FreeVec(rda);
  454.     FreeVec(dalist);
  455.     FreeVec(argbuf);
  456.     FreeVec(strbuf);
  457.     FreeVec(multvec);
  458.     me->pr_Result2=error;
  459.     return NULL;
  460.     }else
  461.     {
  462.     /* All went well. Prepare result and return. */
  463.     rda->RDA_DAList=(IPTR)dalist;
  464.     dalist->ArgBuf=argbuf;
  465.     dalist->StrBuf=strbuf;
  466.     dalist->MultVec=multvec;
  467.     return rda;
  468.     }
  469.     __AROS_FUNC_EXIT
  470. } /* ReadArgs */
  471.